#include "EpollDispatcher.h"

// 实例化EpollDispatcher
EpollDispatcher::EpollDispatcher(EventLoop* evLoop) : Dispatcher(evLoop) {
	_epfd = epoll_create(10);
	if (_epfd == -1) {
		LogDebug("epoll_create error!");
		exit(0);
	}
	_events = new struct epoll_event[_Max];
	_name = "epoll";
}

// 销毁EpollDispatcher
EpollDispatcher::~EpollDispatcher() {
	close(_epfd);
	delete[] _events;
}

// 将[_curChannel.fd]添加到epoll模型
int EpollDispatcher::add() {
	int ret = epollCtl(EPOLL_CTL_ADD);
	if (ret == -1) { LogDebug("epoll_ctl add error!"); }
	return ret;
}

// 将[_curChannel.fd]从epoll模型中删除, 并释放_curChannel所属的TcpConnection
int EpollDispatcher::remove() {
	int ret = epollCtl(EPOLL_CTL_DEL);
	if (ret == -1) { 
		LogDebug("epoll_ctl del error!");
		return ret;
	}

	// 通过channel释放对应的tcpConnection资源
	_curChannel->_destroyCallback(const_cast<void*>(_curChannel->getArg()));
	return ret;
}

// 修改epoll模型中的[_curChannel.fd]的属性
int EpollDispatcher::modify() {
	int ret = epollCtl(EPOLL_CTL_MOD);
	if (ret == -1) { LogDebug("epoll_ctl modify error!"); }
	return ret;
}

// 检测epoll模型上的[_curChannel.fd]是否被激活(是否有就绪事件), 阻塞等待timeout秒, 并处理就绪事件
int EpollDispatcher::dispatch(int timeout) {
	// 1. 等待事件就绪: MAX是_events的容量, count是_events的元素个数
	int count = epoll_wait(_epfd, _events, _Max, timeout * 1000);

	// 2. 处理就绪事件
	for (int i = 0; i < count; ++i) {
		// [1] 取出[被激活的fd]及其就绪事件
		int events = _events[i].events;
		int fd = _events[i].data.fd;

		// [2] 处理就绪事件
		if (events & EPOLLERR || events & EPOLLHUP) { // 对端断开连接 -> 将fd从epoll模型中删除
			// epollRemove(channel, evLoop);
			continue;
		}
		if (events & EPOLLIN) { _eventLoop->eventActive(fd, static_cast<int>(FdEvent::_ReadEvent)); }
		if (events & EPOLLOUT) { _eventLoop->eventActive(fd, static_cast<int>(FdEvent::_WriteEvent)); }
	}
	return 0;
}

// 让_curChannel在evLoop上执行op操作: 用于add、remove、modify
int EpollDispatcher::epollCtl(int op) {
	// 1. 实例化_curChannel对应的epoll_event
	struct epoll_event ev;
	ev.data.fd = _curChannel->getFd();
	ev.events = 0;
	if (_curChannel->getEvents() & static_cast<int>(FdEvent::_ReadEvent)) { ev.events |= EPOLLIN; }
	if (_curChannel->getEvents() & static_cast<int>(FdEvent::_WriteEvent)) { ev.events |= EPOLLOUT; }

	// 2. 让[_curChannel对应的epoll_event]在epoll模型中执行op操作
	int ret = epoll_ctl(_epfd, op, _curChannel->getFd(), &ev);
	return ret;
}
